/*
 * Created on 24.05.2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package wa.application;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import wa.jdk14logging.LogFormatter;


/**
 * @author thavemei
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public abstract class ApplicationImpl {

	//public static Logger log = Logger.getLogger(ApplicationImpl.class.getName());
	final Logger log = LoggerFactory.getLogger(ApplicationImpl.class);
	
	private String appName = "Application";
	private String version = "V0.1";
	private String usageString = AppConst.AUTOMATIC;
	private String fullHelpText=" -v prints out the version"+
								"\n -D(property=value) set an application property"+
								"\n -H hideflag, enables flat mode"+
								"\n -Lf(file) log into file"+
								"\n -Lv(CONFIG|INFO|WARNING|SEVERE) log level"+
						  		"\n -h this help text";
	private int minArgs = -1;
	private int maxArgs = -1;
	private List registeredParams = new ArrayList();
	private HashMap flagMap = new HashMap();
	private HashMap paramsMap = new HashMap();
	
	private String logFile="";
	private String levelString="CONFIG";
	private long timer;
	Properties confFlagValues = new Properties();
	private int termCode = 0;

	public ApplicationImpl()
	{
		addFlag(AppConst.HELPFLAG);
		addFlag(AppConst.VERSIONFLAG);
		addFlag(AppConst.LOGFILEFLAG);
		addFlag(AppConst.LEVELFLAG);
		addFlag(AppConst.HIDEFLAG);
		addFlag(AppConst.CONFFLAG);
	}

	public abstract void preProcess() throws AppException,AppTermination;
	public abstract void process() throws AppException,AppTermination;
	public abstract void postProcess() throws AppException,AppTermination;
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see wa.common.application.ApplicationIf#init(java.lang.String[])
	 */
	public void init(String[] args) throws AppException, AppTermination {
		timer= System.currentTimeMillis();
		
		
		java.util.logging.Logger jdk4log = java.util.logging.Logger.getLogger("");
		Handler[] handlers = jdk4log.getHandlers();
		for (Handler hand : handlers) {
			if (hand instanceof ConsoleHandler) {
				ConsoleHandler cHand = (ConsoleHandler) hand;
				LogFormatter fmt = new LogFormatter();
				
				fmt.setFlatMode(true);
				cHand.setFormatter(fmt);
			}
		}
				

		log.info(getAppName() + " " + getVersion());

		int flagCount = 0;
		if (flagMap.size() > 0) {
			for (int i = 0; i < args.length; i++) {
				if (args[i].startsWith("-")) {
					if (args[i].equals(AppConst.HELPFLAG) || args[i].equals(AppConst.HELPFLAG_ALT1))
					{
						logFullHelp();
						throw new AppTermination(AppConst.NORMAL_TERMINATION);
					} else if (args[i].equals(AppConst.VERSIONFLAG))
					{
						logVersion();
						throw new AppTermination(AppConst.NORMAL_TERMINATION);
					} else if (args[i].indexOf(AppConst.LOGFILEFLAG)==0) {
						logFile = args[i].substring(AppConst.LOGFILEFLAG.length());
						try {
							//Handler fh = new FileHandler(logFile);
							//fh.setFormatter(new LogFormatter());
							//log.addHandler(fh);
							args[i]=AppConst.LOGFILEFLAG;
						} catch (Exception e ) {
							e.printStackTrace();
						} 
					} else if (args[i].indexOf(AppConst.LEVELFLAG)==0) {
						levelString = args[i].substring(AppConst.LEVELFLAG.length());
						//Level level = Level.parse(levelString);
						//log.setLevel(level);
						args[i]=AppConst.LEVELFLAG;
					} else if (args[i].equals(AppConst.HIDEFLAG)) {
						/*Handler[] handlers = log.getHandlers();
						for (int j=0; j<handlers.length; j++) {
							if (handlers[j].getFormatter() instanceof LogFormatter) {
							LogFormatter fmt = (LogFormatter) handlers[j].getFormatter();
							fmt.setFlatMode(true);
							}
						}*/
					}  else if (args[i].indexOf(AppConst.CONFFLAG)==0) {
						//log.config("new config paramter:"+args[i].substring(AppConst.CONFFLAG.length()));
						//Property prop = new Property();
						String confString =args[i].substring(AppConst.CONFFLAG.length()); 
						try{
							String key = confString.substring(0,confString.indexOf('='));
							String value = confString.substring(confString.indexOf('=')+1,confString.length());
							log.debug("key:'"+key+"' value:'"+value+"'");
							confFlagValues.put(key,value);
							args[i]=AppConst.CONFFLAG;
						} catch (StringIndexOutOfBoundsException e) {
							log.warn("Can't parse configuration flag:"+confString);
						}
					}
					
					flagCount++;
					if (flagMap.containsKey(args[i])) {
						String value;
						if (args[i].equals(AppConst.LOGFILEFLAG)) value=logFile;
						else if (args[i].equals(AppConst.LEVELFLAG)) value=levelString;
						else
							value="true";
						flagMap.put(args[i], value);
						log.debug("Flag '" + args[i] + "' with value '"
								+ flagMap.get(args[i]) + "'");
					} else
						log.debug("Ignoring unkown flag '" + args[i] + "' !");
				} else
					i = args.length;
			}
		}

		log.debug("Application flags in total:" + flagCount);

		if (minArgs != (-1)) {
			if (args.length - flagCount < minArgs) {
				log.info("Insufficient arguments ("+ (args.length - flagCount) + " < " + minArgs + ").");
				logShortHelp();
				throw new AppTermination(AppConst.NORMAL_TERMINATION);
			}
		}

		for (int i = flagCount; i < (getArgSet().size() + flagCount); i++) {
			// mandatory parameters
			String myStr = "";
			String paramName = (String) getArgSet().get(i - flagCount);

			try {
				myStr = args[i];
				if (paramName.startsWith("_"))
					paramsMap.put(paramName.substring(1, paramName	.length()), myStr);
				else
					paramsMap.put(paramName, myStr);

				log.debug("Paramter '" + paramName + "' with value '" + myStr+ "'");
			} catch (ArrayIndexOutOfBoundsException e) {
				String lMessage;
				if (((String) paramName).startsWith("_")) {
					// this is no error
					lMessage = "Paramter '" + (paramName).substring(1) +"' with default value '" + getValueSet().get(paramName.substring(1)) + "'.";
					log.debug(lMessage);
				} else {
					lMessage = "Missing mandatory parameter '" + paramName + "'";
					logShortHelp();
					throw new AppException(lMessage);
				}
			}
		}
	}

	public int stop() throws AppException, AppTermination {
		timer = System.currentTimeMillis() - timer;
    	log.debug("Total time:"+timer+" ms");
		log.debug("Application terminated with code ("+getTerminationCode()+").");
		return getTerminationCode();
	}


	/**
	 * @return Returns the appName.
	 */
	public String getAppName() {
		return appName;
	}

	/**
	 * @param appName
	 *            The appName to set.
	 */
	public void setAppName(String appName) {
		this.appName = appName;
	}

	/**
	 * @return Returns the argSet.
	 */
	private List getArgSet() {
		return registeredParams;
	}

	/**
	 * @return Returns the help.
	 */
	public String getUsageString() {
		if (this.usageString.equals(AppConst.AUTOMATIC)) {
			String helpString = "Usage: " + getAppName() + " ";

			// flags

			if (flagMap.keySet().size() > 0) {
				Iterator iter = flagMap.keySet().iterator();
				helpString = helpString + "[";
				while (iter.hasNext()) {
					helpString = helpString + iter.next() + " ";
				}
				helpString = helpString.substring(0, helpString.length() - 1)
						+ "] ";
			}

			// params
			String paramName;
			for (int i = 0; i < getArgSet().size(); i++) {
				paramName = (String) getArgSet().get(i) + " ";
				if (paramName.startsWith("_")) {
					paramName = "["
							+ paramName.substring(1, paramName.length() - 1)
							+ "] ";
				}
				helpString = helpString + paramName;
			}
			return helpString;
		}
		return usageString;
	}

	/**
	 * @param help
	 *            The help to set.
	 */
	public void setUsageString(String help) {
		this.usageString = help;
	}
	
	/**
	 * @return Returns the fullHelpText.
	 */
	public String getFullHelpText() {
		return fullHelpText;
	}
	
	/**
	 * @param fullHelpText The fullHelpText to set.
	 */
	public void setFullHelpText(String fullHelpText) {
		this.fullHelpText = fullHelpText;
	}
	
	public void logShortHelp()
	{
		String helpString = getUsageString() + "\n\tTry "+AppConst.HELPFLAG+" for full help text.";
		log.info(helpString);
	}
	
	public void logFullHelp()
	{
		String helpString = getUsageString()+ "\n"+getFullHelpText();
		log.info(helpString);
	}

	public void logVersion(){
		log.info("Version is: "+getVersion());
	}
	
	
	/**
	 * @return Returns the maxArgs.
	 */
	public int getMaxArgs() {
		return maxArgs;
	}

	/**
	 * @param maxArgs
	 *            The maxArgs to set.
	 */
	public void setMaxArgs(int maxArgs) {
		this.maxArgs = maxArgs;
	}

	/**
	 * @return Returns the minArgs.
	 */
	public int getMinArgs() {
		return minArgs;
	}

	/**
	 * @param minArgs
	 *            The minArgs to set.
	 */
	public void setMinArgs(int minArgs) {
		this.minArgs = minArgs;
	}

	/**
	 * @return Returns the version.
	 */
	public String getVersion() {
		return version;
	}

	/**
	 * @param version
	 *            The version to set.
	 */
	public void setVersion(String version) {
		this.version = version;
	}

	public void addParameter(String pParam) {
		getArgSet().add(pParam);
	}

	public void addOptionalParameter(String pParam, String pDefaultValue) {
		getArgSet().add("_" + pParam);
		getValueSet().put(pParam, pDefaultValue);
	}

	/**
	 * @return Returns the valueSet.
	 */
	private HashMap getValueSet() {
		return paramsMap;
	}

	/**
	 * @param valueSet
	 *            The valueSet to set.
	 */
	private void setValueSet(HashMap valueSet) {
		this.paramsMap = valueSet;
	}

	public String getParam(String pParamName) {
		String value = (String) getValueSet().get(pParamName);
		if (value == null) {
			log.warn("'" + pParamName + "', no such parameter!");
			value = "";
		}
		return value;
	}

	public void addFlag(String pFlagName) {
		this.flagMap.put(pFlagName, "");
	}

	public boolean isFlag(String pFlagName) {
		String value = (String) flagMap.get(pFlagName);
		if (value == null) {
			log.warn("'" + pFlagName + "', no such flag!");
			value = "";
		}
		return value.equals("true");
	}
	
	public boolean isConfFlag(String pFlagName) {
		return confFlagValues.containsKey(pFlagName);
	}

	public String getConfFlagValue(String pFlagName) {
		String value = (String) confFlagValues.get(pFlagName);
		if (value == null) {
			log.warn("'" + pFlagName + "', no such configuration flag!");
			value = "";
		}
		return value;
	}

	public void setTerminationCode(int termCode) {
		this.termCode = termCode;
	}

	public int getTerminationCode() {
		return termCode;
	}

	
}